package com.bsj.travel.device.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsj.travel.cached.redis.RedisCached;
import com.bsj.travel.common.UserCommon;
import com.bsj.travel.config.rocketmq.producer.BaseMqPushUtil;
import com.bsj.travel.constant.BaseServerTopicConstants;
import com.bsj.travel.constant.Constants;
import com.bsj.travel.constant.NumericalConstants;
import com.bsj.travel.constant.ProductConstants;
import com.bsj.travel.constants.RedisConstant;
import com.bsj.travel.def.common.DO.*;
import com.bsj.travel.def.common.DTO.web.product.ProductDTO;
import com.bsj.travel.def.common.DTO.web.product.ProductSearchDTO;
import com.bsj.travel.def.common.VO.ProductVO;
import com.bsj.travel.def.global.JsonResult;
import com.bsj.travel.def.global.JsonResultEnum;
import com.bsj.travel.def.global.mq.Rp;
import com.bsj.travel.device.service.ProductService;
import com.bsj.travel.device.service.TableTDengineService;
import com.bsj.travel.exception.ApiException;
import com.bsj.travel.mapper.mysql.*;
import com.bsj.travel.security.auth.AuthProduct;
import com.bsj.travel.security.service.TokenService;
import com.bsj.travel.util.DateUtils;
import com.bsj.travel.util.LuaScriptUtil;
import com.bsj.travel.util.SecurityUtils;
import com.bsj.travel.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import redis.clients.bsj.RedisEval;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yinhao
 * @version 1.0
 * @description 产品管理
 * @date 2023/11/14
 */
@Service
@Slf4j
public class ProductServiceImpl implements ProductService {

    private static final String RANDOM_STR = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    //产品key前两位固定，采用博实结科技缩写首字母
    private static final String PRODUCT_KEY_STR = "b1";

    @Autowired
    private RedisCached redisCached;

    @Autowired
    private BaseMqPushUtil baseMqPushUtil;

    @Autowired
    private TokenService tokenService;

    @Resource
    private ProductMapper productMapper;

    @Resource
    private DeviceMapper deviceMapper;

    @Resource
    private UserProductMapper userProductMapper;

    @Resource
    private ProductCategoryMapper productCategoryMapper;

    @Resource
    private CategoryMapper categoryMapper;

    @Resource
    private AuthProduct authProduct;

    @Resource
    private ProductTopicMapper productTopicMapper;

    @Resource
    private UserCommon userCommon;

    @Resource
    private TableTDengineService tableTDengineService;


    /**
     * 新增产品信息
     *
     * @param productDTO
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult insertProduct(ProductDTO productDTO) {
        Integer userId = SecurityUtils.getUserId();
        Long categoryId = productDTO.getCategoryId();
        if (categoryId != null) {
            if (categoryMapper.selectById(categoryId) == null) {
                return JsonResult.fail(JsonResultEnum.CATEGORY_IS_NOT_EXIST);
            }
        } else {
            if (productDTO.getCategory().equals(NumericalConstants.TWO)) {
                return JsonResult.fail(JsonResultEnum.CATEGORY_IS_NULL);
            }
        }
        long count = productMapper.selectCount(new QueryWrapper<ProductDO>().eq("productName", productDTO.getProductName()));
        if (count > 0) {
            return JsonResult.fail(JsonResultEnum.PRODUCT_NAME_IS_EXIST);
        }
        paramCheck(productDTO);
        ProductDO productDO = new ProductDO();
        BeanUtils.copyProperties(productDTO, productDO);
        //产品key固定长度为11位
        String productKey = PRODUCT_KEY_STR + RandomUtil.randomString(9);
        productDO.setProductKey(productKey);
        //先新增产品，再根据产品创建对应的设备表
        if (productMapper.insert(productDO) > 0) {
            Long productId = productDO.getProductId();
            //TODO 绑定关系，后续逻辑可能会改
            List<Integer> userIds = userCommon.getUserIds(userId, NumericalConstants.ONE);
            List<UserProductDO> userProductDOS = userIds.stream().map(uid -> {
                return new UserProductDO(uid, productId);
            }).collect(Collectors.toList());
            userProductMapper.batchAddUserProduct(userProductDOS);
            if (categoryId != null) {
                productCategoryMapper.insert(new ProductCategoryDO(productId, categoryId));
            }
            //redis中缓存产品信息
            redisCached.hset(RedisConstant.PRODUCT_INFO_KEY, productKey, JSON.toJSONString(productDO));
            //动态创建对应的设备表
            deviceMapper.createDevTab(Constants.DEVICE_TAB_STR + productKey);
            //TODO 需要将新建的产品信息 mq同步给集群
            Rp<ProductDO> rp = new Rp<>();
            rp.setTag(BaseServerTopicConstants.BASE_CHANGE_PRODUCT_UPDATE_TAG);
            rp.setData(productDO);
            rp.setProductKey(productDO.getProductKey());
            baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.BASE_SERVER_CHANGE_TOPIC, BaseServerTopicConstants.BASE_CHANGE_PRODUCT_UPDATE_TAG, JSON.toJSONBytes(rp));
            return JsonResult.success(productKey);
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 参数校验
     *
     * @param productDTO
     * @return void
     * @author ljx
     * @time 2024/2/20 14:37
     */
    private void paramCheck(ProductDTO productDTO) {
        String nodesType = productDTO.getNodesType();
        String networkType = productDTO.getNetworkType();
        if (!ProductConstants.NODES_TYPE.contains(nodesType)) {
            throw new ApiException(JsonResultEnum.NODES_TYPE_IS_NOT_EXIST);
        }
        if (!ProductConstants.NETWORK_TYPE.contains(networkType)) {
            throw new ApiException(JsonResultEnum.NETWORK_TYPE_IS_NOT_EXIST);
        }
    }

    /**
     * 产品分页
     *
     * @param productSearchDTO
     * @return
     */
    @Override
    public JsonResult<List<ProductVO>> pageProduct(ProductSearchDTO productSearchDTO) {
        int total = 0;
        List<ProductVO> productVOS = new LinkedList<>();
        List<UserProductDO> userProductDOS = userProductMapper.selectList(new QueryWrapper<UserProductDO>().eq("userId", SecurityUtils.getUserId()));
        if (CollectionUtils.isEmpty(userProductDOS)) {
            return JsonResult.success(productVOS, total);
        }
        List<Long> productIds = userProductDOS.stream().map(UserProductDO::getProductId).collect(Collectors.toList());
        IPage<ProductDO> page = new Page<>(productSearchDTO.getPageNumber(), productSearchDTO.getPageSize());
        QueryWrapper<ProductDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("productId", productIds);
        String productName = productSearchDTO.getProductName();
        if (StringUtils.isNotBlank(productName)) {
            queryWrapper.eq("productName", productName);
        }
        IPage<ProductDO> iPage = productMapper.selectPage(page, queryWrapper);
        total = (int) iPage.getTotal();
        List<ProductDO> productDOS = iPage.getRecords();
        if (total == 0) {
            return JsonResult.success(productVOS, total);
        }
        Map<Long, CategoryDO> categoryMap = new HashMap<>();
        Map<Long, ProductCategoryDO> productCategoryMap = new HashMap<>();
        List<Long> productIdList = productDOS.stream().map(ProductDO::getProductId).collect(Collectors.toList());
        List<ProductCategoryDO> productCategoryDOS = productCategoryMapper.selectList(Wrappers.<ProductCategoryDO>lambdaQuery().in(ProductCategoryDO::getProductId, productIdList));
        if (!CollectionUtils.isEmpty(productCategoryDOS)) {
            productCategoryMap = productCategoryDOS.stream().collect(Collectors.toMap(ProductCategoryDO::getProductId, a -> a, (k1, k2) -> k1));
            List<Long> categoryIds = productCategoryDOS.stream().map(ProductCategoryDO::getCategoryId).collect(Collectors.toList());
            List<CategoryDO> categoryDOS = categoryMapper.selectList(Wrappers.<CategoryDO>lambdaQuery().in(CategoryDO::getCategoryId, categoryIds));
            if (!CollectionUtils.isEmpty(categoryDOS)) {
                categoryMap = categoryDOS.stream().collect(Collectors.toMap(CategoryDO::getCategoryId, a -> a, (k1, k2) -> k1));
            }
        }
        for (ProductDO productDO : productDOS) {
            ProductVO productVO = new ProductVO();
            BeanUtils.copyProperties(productDO, productVO);
            productVO.setCreateTime(DateUtils.parseDateToStr(productDO.getCreateTime()));
            ProductCategoryDO productCategoryDO = productCategoryMap.get(productDO);
            if (productCategoryDO != null) {
                CategoryDO categoryDO = categoryMap.get(productCategoryDO.getCategoryId());
                if (categoryDO != null) {
                    productVO.setCategoryName(categoryDO.getCategoryName());
                }
            }
            productVOS.add(productVO);
        }
        return JsonResult.success(productVOS, total);
    }

    /**
     * 删除产品信息
     *
     * @param productId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteProductById(Long productId) throws Exception {
        if (productId == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        authProduct.permissionCheck(productId);
        ProductDO productDO = productMapper.selectById(productId);
        if (productDO == null) {
            throw new ApiException(JsonResultEnum.PRODUCT_IS_NOT_EXIST);
        }

        List<RedisEval> redisEvalList = new ArrayList<>();
        String productKey = productDO.getProductKey();
        //TODO 1.先要删除设备表信息，设备相关数据
        String tableName = Constants.DEVICE_TAB_STR + productKey;
        long deviceCount = deviceMapper.selectDeviceAllCount(Map.of("tableName", tableName));
        if (deviceCount > 0) {
            throw new ApiException(JsonResultEnum.DEVICE_IS_NOT_NULL);
        }
        deviceMapper.deleteDevTab(tableName);
        //TODO 2.再删除产品信息，产品相关数据
        int deleteById = productMapper.deleteById(productDO.getProductId());
        //删除绑定关系
        userProductMapper.delete(new QueryWrapper<UserProductDO>().eq("productId", productId));
        productCategoryMapper.delete(new QueryWrapper<ProductCategoryDO>().eq("productId", productId));
        productTopicMapper.delete(new QueryWrapper<ProductTopicDO>().eq("productId", productId));
        //用于批量操作redis
        RedisEval eVal = new RedisEval();
        List<String> keys = eVal.getKeys();
        List<String> param = eVal.getParam();
        keys.add(RedisConstant.PRODUCT_INFO_KEY);
        keys.add(RedisConstant.PRODUCT_CUSTOMIZE_TOPIC_KEY + productKey);
        param.add(productKey);
        redisEvalList.add(eVal);
        redisCached.mUniqueEval(LuaScriptUtil.delProductInfo(), redisEvalList);
        Rp<ProductDO> rp = new Rp<>();
        rp.setTag(BaseServerTopicConstants.BASE_CHANGE_PRODUCT_DELETE_TAG);
        rp.setData(productDO);
        rp.setProductKey(productDO.getProductKey());
        baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.BASE_SERVER_CHANGE_TOPIC, BaseServerTopicConstants.BASE_CHANGE_PRODUCT_DELETE_TAG, JSON.toJSONBytes(rp));
        return deleteById;
    }

    /**
     * 查询产品管理列表
     *
     * @param type 1查所有 2查当前登录用户所有
     * @return java.util.List<com.bsj.travel.def.common.DO.ProductDO>
     */
    @Override
    public List<ProductDO> selectProductList(Integer type) {
        if (type.equals(NumericalConstants.ONE)) {
            return productMapper.selectList(Wrappers.<ProductDO>lambdaQuery().orderByDesc(ProductDO::getCreateTime));
        } else if (type.equals(NumericalConstants.TWO)) {
            Integer userId = SecurityUtils.getUserId();
            List<UserProductDO> userProductDOS = userProductMapper.selectList(new QueryWrapper<UserProductDO>().eq("userId", userId));
            if (CollectionUtils.isEmpty(userProductDOS)) {
                return null;
            }
            List<Long> productIds = userProductDOS.stream().map(UserProductDO::getProductId).collect(Collectors.toList());
            return productMapper.selectList(Wrappers.<ProductDO>lambdaQuery().in(ProductDO::getProductId, productIds).orderByDesc(ProductDO::getCreateTime));
        } else {
            return null;
        }
    }

    /**
     * 修改产品
     *
     * @param productDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/17 15:22
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult updateProduct(ProductDTO productDTO) {
        Long productId = productDTO.getProductId();
        if (productId == null) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        authProduct.permissionCheck(productId);
        ProductDO oldProductDO = productMapper.selectById(productId);
        if (oldProductDO == null) {
            return JsonResult.fail(JsonResultEnum.PRODUCT_IS_NOT_EXIST);
        }
        paramCheck(productDTO);
        //品类无法更改
        Integer category = productDTO.getCategory();
        if (!oldProductDO.getCategory().equals(category)) {
            return JsonResult.fail(JsonResultEnum.CATEGORY_CANNOT_BE_UPDATE);
        }
        if (category.equals(NumericalConstants.TWO)) {
            Long categoryId = productDTO.getCategoryId();
            if (categoryId == null) {
                return JsonResult.fail(JsonResultEnum.CATEGORY_IS_NULL);
            }
            ProductCategoryDO productCategoryDO = productCategoryMapper.selectOne(new QueryWrapper<ProductCategoryDO>().eq("productId", productId));
            if (!productCategoryDO.getCategoryId().equals(categoryId)) {
                return JsonResult.fail(JsonResultEnum.CATEGORY_CANNOT_BE_UPDATE);
            }
        }
        ProductDO productDO = new ProductDO();
        BeanUtils.copyProperties(productDTO, productDO);
        if (productMapper.updateById(productDO) > 0) {
            //redis处理
            redisCached.hset(RedisConstant.PRODUCT_INFO_KEY, oldProductDO.getProductKey(), JSON.toJSONString(productDO));
            //mq同步给集群
            Rp<ProductDO> rp = new Rp<>();
            rp.setTag(BaseServerTopicConstants.BASE_CHANGE_PRODUCT_UPDATE_TAG);
            rp.setData(productDO);
            rp.setProductKey(productDO.getProductKey());
            baseMqPushUtil.sendTopicBytes(BaseServerTopicConstants.BASE_SERVER_CHANGE_TOPIC, BaseServerTopicConstants.BASE_CHANGE_PRODUCT_UPDATE_TAG, JSON.toJSONBytes(rp));
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 根据产品id获取产品信息
     *
     * @param productId
     * @return com.bsj.travel.def.common.VO.ProductVO
     * @author ljx
     * @time 2024/1/17 15:22
     */
    @Override
    public ProductVO getProductInfoById(Long productId) {
        if (productId == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        authProduct.permissionCheck(productId);
        ProductDO productDO = productMapper.selectById(productId);
        if (productDO == null) {
            throw new ApiException(JsonResultEnum.PRODUCT_IS_NOT_EXIST);
        }
        ProductVO productVO = new ProductVO();
        BeanUtils.copyProperties(productDO, productVO);
        ProductCategoryDO productCategoryDO = productCategoryMapper.selectOne(Wrappers.<ProductCategoryDO>lambdaQuery().eq(ProductCategoryDO::getProductId, productId));
        if (productCategoryDO != null) {
            CategoryDO categoryDO = categoryMapper.selectById(productCategoryDO.getCategoryId());
            if (categoryDO != null) {
                productVO.setCategoryId(categoryDO.getCategoryId());
                productVO.setCategoryName(categoryDO.getCategoryName());
            }
        }
        return productVO;
    }

}
